package java_thinking.unit_10._10_8;

/**
 * @Description : 闭包例子：进一步展示外围类实现一个接口与内部类实现此接口之间的区别
 * 什么是闭包，大白话不怎么严谨的说就是：
 *      1.一个依赖于外部环境自由变量的函数
 *      2.这个函数能够访问外部环境里的自由变量
 *
 * 一定要看的参考：https://www.zhihu.com/question/21395848
 *
 *
 * 内部类（Inner Class）通过包含一个指向外部类的引用，做到自由访问外部环境类的所有字段，变相把环境中的自由变量封装到函数里，形成一个闭包
 * @author: cww
 * @DateTime: 2019-03-12 22:28
 */
interface Incrementable {
    void increment();
}

class Callee1 implements Incrementable {
    private int i = 0;
    @Override
    public void increment(){
        i++;
        System.out.println(i);
    }
}

class MyIncrement {
    public void increment(){
        System.out.println("Other operation");
    }
    static void f(MyIncrement myIncrement){
        myIncrement.increment();
    }
}

class Callee2 extends MyIncrement {
    private int i = 0;
    @Override
    public void increment(){
        super.increment();
        i++;
        System.out.println(i);
    }

    /**
     * 一个对象需要继承于Incrementable，但是他又要用到另外一个类的同名方法，
     * 这时候就要用内部类
     * 当创建一个内部类时，并没有在外围类的接口中添加东西，也没有修改外围类的接口
     * 这里Closure提供了一个Callee2的一个回调
     * 回调的价值在于它的灵活性，可以在运行时动态决定需要调用什么方法！！！！
     */
    private class Closure implements Incrementable {

        @Override
        public void increment() {
            Callee2.this.increment();
        }
    }
    Incrementable getCallbackReference(){
        return new Closure();
    }
}
class Caller {
    private Incrementable callbackReference;
    Caller(Incrementable cbh){
        callbackReference = cbh;
    }
    void go(){
        callbackReference.increment();
    }
}
class Callbacks{
    public static void main(String[] args) {
        Callee1 c1 = new Callee1();
        c1.increment();
        Callee2 c2 = new Callee2();
        MyIncrement.f(c2);
        Caller caller1 = new Caller(c1);
        /**
         * 这里Caller与Callee2没有任何关联(继承或实现)，但是此时需要Caller，但是又要执行Callee2的increment方法
         * 这时候就要用内部类Closure来实现内部类调用外围类方法
         */
        Caller caller2 = new Caller(c2.getCallbackReference());
        caller1.go();
        caller1.go();
        caller2.go();
        caller2.go();

    }
}
